package com.thinkgem.jeesite.common.license;

import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStreamReader;
import java.math.BigInteger;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.security.KeyFactory;
import java.security.PublicKey;
import java.security.Security;
import java.security.Signature;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.RSAPublicKeySpec;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Locale;

import javax.crypto.Cipher;

import org.apache.commons.codec.binary.Base64;
import org.apache.commons.io.FileUtils;
import org.apache.commons.lang3.StringUtils;
import org.bouncycastle.jce.provider.BouncyCastleProvider;

import com.thinkgem.jeesite.common.license.exception.LicenseInvalidException;

public abstract class RSADeCoder{
	public static final String KEY_ALGORITHM = "RSA";
	public static final String KEY_PROVIDER = "BC";
	public static final String SIGNATURE_ALGORITHM = "SHA1WithRSA";
	 
	
	/**
	 * 公钥解密
	 * */
	public static byte[] decryptRSA(byte[] encoderData,PublicKey publicKey) throws Exception {
		Cipher cipher = Cipher.getInstance(KEY_ALGORITHM, KEY_PROVIDER);
		cipher.init(Cipher.DECRYPT_MODE, publicKey);
		int blockSize = cipher.getBlockSize();
		ByteArrayOutputStream bout = new ByteArrayOutputStream();
		int j = 0;
		while (encoderData.length - j * blockSize > 0) {
		    bout.write(cipher.doFinal(encoderData, j * blockSize, blockSize));
		    j++;
		}
		return bout.toByteArray();
	}
	
	/**
	 * 校验数字签名
	 * */
	public static boolean verify (byte[] encoderData,String sign,PublicKey publicKey) throws Exception {
		
		Signature sig = Signature.getInstance(SIGNATURE_ALGORITHM,KEY_PROVIDER);
		sig.initVerify(publicKey);
		sig.update(encoderData);
		return sig.verify(Base64.decodeBase64(sign.getBytes()));
	}
	
	/** 
	 * 使用模和指数生成RSA公钥 
	 * @param modulus  模 
	 * @param exponent  指数 
	 * @return 
	 */  
	public static PublicKey getPublicKey(String modulus, String exponent) throws Exception {  
	        BigInteger b1 = new BigInteger(modulus);  
	        BigInteger b2 = new BigInteger(exponent);  
	        Security.addProvider(new BouncyCastleProvider());
	        KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM, KEY_PROVIDER);
	        RSAPublicKeySpec keySpec = new RSAPublicKeySpec(b1, b2);  
	        return (RSAPublicKey) keyFactory.generatePublic(keySpec);  
	}  
	
	public static boolean checkLicenseFile(String licenseFilePath, Date currentDate)
			throws Exception {
		//得到mac地址
		InetAddress ip = InetAddress.getLocalHost();
		byte[] mac = NetworkInterface.getByInetAddress(ip).getHardwareAddress();
		StringBuffer sb = new StringBuffer("");
		for (int i = 0; i < mac.length; i++) {
			if (i != 0) {
				sb.append("-");
			}
			// 字节转换为整数
			int temp = mac[i] & 0xff;
			String str = Integer.toHexString(temp);
			if (str.length() == 1) {
				sb.append("0" + str);
			} else {
				sb.append(str);
			}
		}
		String machCode = StringUtils.isEmpty(sb.toString())?"":sb.toString().toUpperCase();
		// 加载证书
		PublicKey publicKey = getPublicKey(CryptoPublicKeys.KEYMODULUS, CryptoPublicKeys.PUBLICKEYEXPONENT);
		File licenseFile = new File(licenseFilePath);
		if (!licenseFile.exists()) {
			throw new Exception("没有获取License文件");
		}
		FileInputStream fis = FileUtils.openInputStream(licenseFile);
		if (fis.available() == 0) {
			throw new FileNotFoundException("License加密文件为空");
		}
		fis.close();
		// 首先读入License文件
		byte[] encoderData = FileUtils.readFileToByteArray(licenseFile);
		byte[] sbytes = RSADeCoder.decryptRSA(encoderData, publicKey);
		byte[] firstEncoderData = null;
		byte[] secondSignData = null;
		int index = 0;
		for (int len = sbytes.length; index < len; index++) {
			if (sbytes[index] == '\n') {
				firstEncoderData = new byte[index];
				secondSignData = new byte[len - index - 1];
				// 合并两个byte[]
				System.arraycopy(sbytes, 0, firstEncoderData, 0, firstEncoderData.length);
				System.arraycopy(sbytes, index + 1, secondSignData, 0, secondSignData.length);
				// 如果已经是base64编码， 则直接返回
				if (isArrayByteBase64(secondSignData)) {
					break;
				}
			}
		}
		String sign = new String(secondSignData);
		// System.out.println("sbytes:"+new String(sbytes) );
		// System.out.println("sign:"+sign);

		boolean verify = RSADeCoder.verify(firstEncoderData, sign, publicKey);
		if (!verify) {
			throw new LicenseInvalidException("License签名校验无效,请确认这证书的有效性.");
		}
		// 再次解密， 得到最终的授权内容
		byte[] textBytes = RSADeCoder.decryptRSA(firstEncoderData, publicKey);
		HashMap<String, String> prop = genDataFromArrayByte(textBytes);
		String licenseType = prop.get("LICENSETYPE");
		if (StringUtils.isEmpty(licenseType)) {
			throw new LicenseInvalidException("License校验无效,无法获取证书类型.");
		}
		String licenseKey = prop.get("LICENSEID");
		String licenseName = prop.get("LICENSENAME");

		if (!licenseKey.toUpperCase().startsWith("GXPTOOL")) {
			throw new LicenseInvalidException("License校验无效,请确认这证书的有效性.");
		}

		String machineCode = prop.get("MACHINECODE");
		String needCheckMachineCode = prop.get("CHECKMACHINECODE");
		if ((!"NO".equalsIgnoreCase(needCheckMachineCode))
				&& (!"demo".equals(licenseName))
				&& (StringUtils.isEmpty(machineCode) || ("," + machineCode + ",").indexOf("," + machCode + ",") < 0)) {
			// --机器码的校验
			throw new LicenseInvalidException("License校验,机器码与当前机器码不同");
		}
		if ("2".equals(licenseType)) {
			DateFormat df = new SimpleDateFormat("yyyy-MM-dd", Locale.ENGLISH);
			// new SimpleDateFormat("MMM dd HH:mm:ss 'UTC'Z
			// yyyy",Locale.ENGLISH);
			String expireDay = prop.get("EXPIREDAY");
			Date expireDate = df.parse(expireDay);
			if (currentDate.after(expireDate)) {
				throw new LicenseInvalidException("License已经过期,请联系厂商重新获取License授权.");
			}
		}
		// 检查数据项：授权打印终端数量
		String printClientCount = prop.get("PRINTCLIENTCOUNT");
		if (StringUtils.isEmpty(printClientCount)) {
			throw new LicenseInvalidException("授权打印终端数量必须填写");
		}

		if (!StringUtils.isNumeric(printClientCount.trim())) {
			throw new LicenseInvalidException("授权打印终端数量必须为数字");
		}
		return true;

	}

	private static boolean isArrayByteBase64(byte[] b) {
		try {
			if (Base64.isArrayByteBase64(b)) {
				return true;
			}
			return false;
		} catch (Exception e) {// nothing todo
			return false;
		}
	}

	private static HashMap<String, String> genDataFromArrayByte(byte[] b) throws IOException {
		BufferedReader br = new BufferedReader(new InputStreamReader(new ByteArrayInputStream(b)));
		HashMap<String, String> data = new HashMap<String, String>();
		String str = null;
		while ((str = br.readLine()) != null) {
			if (StringUtils.isNotEmpty(str)) {
				str = str.trim();
				int pos = str.indexOf("=");
				if (pos <= 0)
					continue;
				if (str.length() > pos + 1) {
					data.put(str.substring(0, pos).trim().toUpperCase(), str.substring(pos + 1).trim());
				} else {
					data.put(str.substring(0, pos).trim().toUpperCase(), "");
				}
			}
		}
		return data;
	}
}